home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 151 / cd-rom 151.iso / internet / firefox / Firefox Setup 3.0 Beta 1.exe / nonlocalized / components / nsLoginManagerPrompter.js < prev    next >
Encoding:
Text File  |  2007-11-09  |  26.4 KB  |  754 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is mozilla.org code.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla Corporation.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Justin Dolske <dolske@mozilla.com> (original author)
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37.  
  38. const Cc = Components.classes;
  39. const Ci = Components.interfaces;
  40.  
  41. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  42.  
  43. /*
  44.  * LoginManagerPromptFactory
  45.  *
  46.  * Implements nsIPromptFactory
  47.  *
  48.  * Invoked by NS_NewAuthPrompter2()
  49.  * [embedding/components/windowwatcher/src/nsPrompt.cpp]
  50.  */
  51. function LoginManagerPromptFactory() {}
  52.  
  53. LoginManagerPromptFactory.prototype = {
  54.  
  55.     classDescription : "LoginManagerPromptFactory",
  56.     contractID : "@mozilla.org/passwordmanager/authpromptfactory;1",
  57.     classID : Components.ID("{749e62f4-60ae-4569-a8a2-de78b649660e}"),
  58.     QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory]),
  59.  
  60.     getPrompt : function (aWindow, aIID) {
  61.         var prompt = new LoginManagerPrompter().QueryInterface(aIID);
  62.         prompt.init(aWindow);
  63.         return prompt;
  64.     }
  65. }; // end of LoginManagerPromptFactory implementation
  66.  
  67.  
  68.  
  69.  
  70. /* ==================== LoginManagerPrompter ==================== */
  71.  
  72.  
  73.  
  74.  
  75. /*
  76.  * LoginManagerPrompter
  77.  *
  78.  * Implements nsIAuthPrompt2 and nsILoginManagerPrompter.
  79.  * nsIAuthPrompt2 usage is invoked by a channel for protocol-based
  80.  * authentication (eg HTTP Authenticate, FTP login). nsILoginManagerPrompter
  81.  * is invoked by Login Manager for saving/changing a login.
  82.  */
  83. function LoginManagerPrompter() {}
  84.  
  85. LoginManagerPrompter.prototype = {
  86.  
  87.     classDescription : "LoginManagerPrompter",
  88.     contractID : "@mozilla.org/login-manager/prompter;1",
  89.     classID : Components.ID("{8aa66d77-1bbb-45a6-991e-b8f47751c291}"),
  90.     QueryInterface : XPCOMUtils.generateQI(
  91.                         [Ci.nsIAuthPrompt2, Ci.nsILoginManagerPrompter]),
  92.  
  93.     _window        : null,
  94.     _debug         : false,
  95.  
  96.     __pwmgr : null, // Password Manager service
  97.     get _pwmgr() {
  98.         if (!this.__pwmgr)
  99.             this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
  100.                            getService(Ci.nsILoginManager);
  101.         return this.__pwmgr;
  102.     },
  103.  
  104.     __logService : null, // Console logging service, used for debugging.
  105.     get _logService() {
  106.         if (!this.__logService)
  107.             this.__logService = Cc["@mozilla.org/consoleservice;1"].
  108.                                 getService(Ci.nsIConsoleService);
  109.         return this.__logService;
  110.     },
  111.  
  112.     __promptService : null, // Prompt service for user interaction
  113.     get _promptService() {
  114.         if (!this.__promptService)
  115.             this.__promptService =
  116.                 Cc["@mozilla.org/embedcomp/prompt-service;1"].
  117.                 getService(Ci.nsIPromptService2);
  118.         return this.__promptService;
  119.     },
  120.  
  121.  
  122.     __strBundle : null, // String bundle for L10N
  123.     get _strBundle() {
  124.         if (!this.__strBundle) {
  125.             var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
  126.                              getService(Ci.nsIStringBundleService);
  127.             this.__strBundle = bunService.createBundle(
  128.                         "chrome://passwordmgr/locale/passwordmgr.properties");
  129.             if (!this.__strBundle)
  130.                 throw "String bundle for Login Manager not present!";
  131.         }
  132.  
  133.         return this.__strBundle;
  134.     },
  135.  
  136.  
  137.     __brandBundle : null, // String bundle for L10N
  138.     get _brandBundle() {
  139.         if (!this.__brandBundle) {
  140.             var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
  141.                              getService(Ci.nsIStringBundleService);
  142.             this.__brandBundle = bunService.createBundle(
  143.                         "chrome://branding/locale/brand.properties");
  144.             if (!this.__brandBundle)
  145.                 throw "Branding string bundle not present!";
  146.         }
  147.  
  148.         return this.__brandBundle;
  149.     },
  150.  
  151.  
  152.     /*
  153.      * log
  154.      *
  155.      * Internal function for logging debug messages to the Error Console window.
  156.      */
  157.     log : function (message) {
  158.         if (!this._debug)
  159.             return;
  160.  
  161.         dump("Pwmgr Prompter: " + message + "\n");
  162.         this._logService.logStringMessage("Pwmgr Prompter: " + message);
  163.     },
  164.  
  165.  
  166.  
  167.  
  168.     /* ---------- nsIAuthPrompt2 prompts ---------- */
  169.  
  170.  
  171.  
  172.  
  173.     /*
  174.      * promptAuth
  175.      *
  176.      * Implementation of nsIAuthPrompt2.
  177.      *
  178.      * nsIChannel aChannel
  179.      * int        aLevel
  180.      * nsIAuthInformation aAuthInfo
  181.      */
  182.     promptAuth : function (aChannel, aLevel, aAuthInfo) {
  183.         var selectedLogin = null;
  184.         var checkbox = { value : false };
  185.         var checkboxLabel = null;
  186.         var epicfail = false;
  187.  
  188.         try {
  189.  
  190.             this.log("===== promptAuth called =====");
  191.  
  192.             // If the user submits a login but it fails, we need to remove the
  193.             // notification bar that was displayed. Conveniently, the user will
  194.             // be prompted for authentication again, which brings us here.
  195.             // XXX this isn't right if there are multiple logins on a page (eg,
  196.             // 2 images from different http realms). That seems like an edge
  197.             // case that we're probably not handling right anyway.
  198.             var notifyBox = this._getNotifyBox();
  199.             if (notifyBox)
  200.                 this._removeSaveLoginNotification(notifyBox);
  201.  
  202.             var hostname, httpRealm;
  203.             [hostname, httpRealm] = this._GetAuthKey(aChannel, aAuthInfo);
  204.  
  205.  
  206.             // Looks for existing logins to prefill the prompt with.
  207.             var foundLogins = this._pwmgr.findLogins({},
  208.                                         hostname, null, httpRealm);
  209.  
  210.             // XXX Like the original code, we can't deal with multiple
  211.             // account selection. (bug 227632)
  212.             if (foundLogins.length > 0) {
  213.                 selectedLogin = foundLogins[0];
  214.                 this._SetAuthInfo(aAuthInfo, selectedLogin.username,
  215.                                              selectedLogin.password);
  216.                 checkbox.value = true;
  217.             }
  218.  
  219.             var canRememberLogin = this._pwmgr.getLoginSavingEnabled(hostname);
  220.         
  221.             // if checkboxLabel is null, the checkbox won't be shown at all.
  222.             if (canRememberLogin && !notifyBox)
  223.                 checkboxLabel = this._getLocalizedString("rememberPassword");
  224.         } catch (e) {
  225.             // Ignore any errors and display the prompt anyway.
  226.             epicfail = true;
  227.             Components.utils.reportError("LoginManagerPrompter: " +
  228.                 "Epic fail in promptAuth: " + e + "\n");
  229.         }
  230.  
  231.         var ok = this._promptService.promptAuth(this._window, aChannel,
  232.                                 aLevel, aAuthInfo, checkboxLabel, checkbox);
  233.         if (epicfail)
  234.             return ok;
  235.  
  236.         try {
  237.             // If there's a notification box, use it to allow the user to
  238.             // determine if the login should be saved. If there isn't a
  239.             // notification box, only save the login if the user set the
  240.             // checkbox to do so.
  241.             var rememberLogin = notifyBox ? canRememberLogin : checkbox.value;
  242.  
  243.             if (ok && rememberLogin) {
  244.                 var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  245.                                createInstance(Ci.nsILoginInfo);
  246.                 newLogin.init(hostname, null, httpRealm,
  247.                               aAuthInfo.username, aAuthInfo.password,
  248.                               "", "");
  249.  
  250.                 // If we didn't find an existing login, or if the username
  251.                 // changed, save as a new login.
  252.                 if (!selectedLogin ||
  253.                     aAuthInfo.username != selectedLogin.username) {
  254.  
  255.                     // add as new
  256.                     this.log("New login seen for " + aAuthInfo.username +
  257.                              " @ " + hostname + " (" + httpRealm + ")");
  258.                     if (notifyBox)
  259.                         this._showSaveLoginNotification(notifyBox, newLogin);
  260.                     else
  261.                         this._pwmgr.addLogin(newLogin);
  262.  
  263.                 } else if (selectedLogin &&
  264.                            aAuthInfo.password != selectedLogin.password) {
  265.  
  266.                     this.log("Updating password for " + aAuthInfo.username +
  267.                              " @ " + hostname + " (" + httpRealm + ")");
  268.                     // update password
  269.                     this._pwmgr.modifyLogin(foundLogins[0], newLogin);
  270.  
  271.                 } else {
  272.                     this.log("Login unchanged, no further action needed.");
  273.                     return ok;
  274.                 }
  275.             }
  276.         } catch (e) {
  277.             Components.utils.reportError("LoginManagerPrompter: " +
  278.                 "Fail2 in promptAuth: " + e + "\n");
  279.         }
  280.  
  281.         return ok;
  282.     },
  283.  
  284.     asyncPromptAuth : function () {
  285.         return NS_ERROR_NOT_IMPLEMENTED;
  286.     },
  287.  
  288.  
  289.  
  290.  
  291.     /* ---------- nsILoginManagerPrompter prompts ---------- */
  292.  
  293.  
  294.  
  295.  
  296.     /*
  297.      * init
  298.      *
  299.      */
  300.     init : function (aWindow) {
  301.         this._window = aWindow;
  302.         this.log("===== initialized =====");
  303.     },
  304.  
  305.  
  306.     /*
  307.      * promptToSavePassword
  308.      *
  309.      */
  310.     promptToSavePassword : function (aLogin) {
  311.         var notifyBox = this._getNotifyBox();
  312.  
  313.         if (notifyBox)
  314.             this._showSaveLoginNotification(notifyBox, aLogin);
  315.         else
  316.             this._showSaveLoginDialog(aLogin);
  317.     },
  318.  
  319.  
  320.     /*
  321.      * _showSaveLoginNotification
  322.      *
  323.      * Displays a notification bar (rather than a popup), to allow the user to
  324.      * save the specified login. This allows the user to see the results of
  325.      * their login, and only save a login which they know worked.
  326.      *
  327.      */
  328.     _showSaveLoginNotification : function (aNotifyBox, aLogin) {
  329.  
  330.         // Ugh. We can't use the strings from the popup window, because they
  331.         // have the access key marked in the string (eg "Mo&zilla"), along
  332.         // with some weird rules for handling access keys that do not occur
  333.         // in the string, for L10N. See commonDialog.js's setLabelForNode().
  334.         var neverButtonText =
  335.               this._getLocalizedString("notifyBarNeverForSiteButtonText");
  336.         var neverButtonAccessKey =
  337.               this._getLocalizedString("notifyBarNeverForSiteButtonAccessKey");
  338.         var rememberButtonText =
  339.               this._getLocalizedString("notifyBarRememberButtonText");
  340.         var rememberButtonAccessKey =
  341.               this._getLocalizedString("notifyBarRememberButtonAccessKey");
  342.  
  343.         var brandShortName =
  344.               this._brandBundle.GetStringFromName("brandShortName");
  345.         var notificationText  = this._getLocalizedString(
  346.                                         "savePasswordText", [brandShortName]);
  347.  
  348.         // The callbacks in |buttons| have a closure to access the variables
  349.         // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
  350.         // without a getService() call.
  351.         var pwmgr = this._pwmgr;
  352.  
  353.  
  354.         var buttons = [
  355.             // "Remember" button
  356.             {
  357.                 label:     rememberButtonText,
  358.                 accessKey: rememberButtonAccessKey,
  359.                 popup:     null,
  360.                 callback: function(aNotificationBar, aButton) {
  361.                     pwmgr.addLogin(aLogin);
  362.                 }
  363.             },
  364.  
  365.             // "Never for this site" button
  366.             {
  367.                 label:     neverButtonText,
  368.                 accessKey: neverButtonAccessKey,
  369.                 popup:     null,
  370.                 callback: function(aNotificationBar, aButton) {
  371.                     pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  372.                 }
  373.             }
  374.  
  375.             // "Not now" button not needed, as notification bar isn't modal.
  376.         ];
  377.  
  378.  
  379.         var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  380.         const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
  381.  
  382.         this.log("Adding new save-password notification bar");
  383.         var newBar = aNotifyBox.appendNotification(
  384.                                 notificationText, "password-save",
  385.                                 null, priority, buttons);
  386.  
  387.         // The page we're going to hasn't loaded yet, so we want to persist
  388.         // across the first location change.
  389.         newBar.ignoreFirstLocationChange = true;
  390.  
  391.         // Sites like Gmail perform a funky redirect dance before you end up
  392.         // at the post-authentication page. I don't see a good way to
  393.         // heuristically determine when to ignore such location changes, so
  394.         // we'll try ignoring location changes based on a time interval.
  395.         var now = Date.now() / 1000;
  396.         newBar.ignoreLocationChangeTimeout = now + 10; // 10 seconds
  397.  
  398.         if (oldBar) {
  399.             this.log("(...and removing old save-password notification bar)");
  400.             aNotifyBox.removeNotification(oldBar);
  401.         }
  402.     },
  403.  
  404.  
  405.     /*
  406.      * _removeSaveLoginNotification
  407.      *
  408.      */
  409.     _removeSaveLoginNotification : function (aNotifyBox) {
  410.  
  411.         var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  412.  
  413.         if (oldBar) {
  414.             this.log("Removing save-password notification bar.");
  415.             aNotifyBox.removeNotification(oldBar);
  416.         }
  417.     },
  418.  
  419.  
  420.     /*
  421.      * _showSaveLoginDialog
  422.      *
  423.      * Called when we detect a new login in a form submission,
  424.      * asks the user what to do.
  425.      *
  426.      */
  427.     _showSaveLoginDialog : function (aLogin) {
  428.         const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
  429.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
  430.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
  431.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2);
  432.  
  433.         var brandShortName =
  434.                 this._brandBundle.GetStringFromName("brandShortName");
  435.  
  436.         var dialogText         = this._getLocalizedString(
  437.                                         "savePasswordText", [brandShortName]);
  438.         var dialogTitle        = this._getLocalizedString(
  439.                                         "savePasswordTitle");
  440.         var neverButtonText    = this._getLocalizedString(
  441.                                         "neverForSiteButtonText");
  442.         var rememberButtonText = this._getLocalizedString(
  443.                                         "rememberButtonText");
  444.         var notNowButtonText   = this._getLocalizedString(
  445.                                         "notNowButtonText");
  446.  
  447.         this.log("Prompting user to save/ignore login");
  448.         var userChoice = this._promptService.confirmEx(this._window,
  449.                                             dialogTitle, dialogText,
  450.                                             buttonFlags, rememberButtonText,
  451.                                             notNowButtonText, neverButtonText,
  452.                                             null, {});
  453.         //  Returns:
  454.         //   0 - Save the login
  455.         //   1 - Ignore the login this time
  456.         //   2 - Never save logins for this site
  457.         if (userChoice == 2) {
  458.             this.log("Disabling " + aLogin.hostname + " logins by request.");
  459.             this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  460.         } else if (userChoice == 0) {
  461.             this.log("Saving login for " + aLogin.hostname);
  462.             this._pwmgr.addLogin(aLogin);
  463.         } else {
  464.             // userChoice == 1 --> just ignore the login.
  465.             this.log("Ignoring login.");
  466.         }
  467.     },
  468.  
  469.  
  470.     /*
  471.      * promptToChangePassword
  472.      *
  473.      * Called when we think we detect a password change for an existing
  474.      * login, when the form being submitted contains multiple password
  475.      * fields.
  476.      *
  477.      */
  478.     promptToChangePassword : function (aOldLogin, aNewLogin) {
  479.         const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  480.  
  481.         var dialogText;
  482.         if (aOldLogin.username)
  483.             dialogText  = this._getLocalizedString(
  484.                                     "passwordChangeText",
  485.                                     [aOldLogin.username]);
  486.         else
  487.             dialogText  = this._getLocalizedString(
  488.                                     "passwordChangeTextNoUser");
  489.  
  490.         var dialogTitle = this._getLocalizedString(
  491.                                     "passwordChangeTitle");
  492.  
  493.         // returns 0 for yes, 1 for no.
  494.         var ok = !this._promptService.confirmEx(this._window,
  495.                                 dialogTitle, dialogText, buttonFlags,
  496.                                 null, null, null,
  497.                                 null, {});
  498.         if (ok) {
  499.             this.log("Updating password for user " + aOldLogin.username);
  500.             this._pwmgr.modifyLogin(aOldLogin, aNewLogin);
  501.         }
  502.     },
  503.  
  504.  
  505.     /*
  506.      * promptToChangePasswordWithUsernames
  507.      *
  508.      * Called when we detect a password change in a form submission, but we
  509.      * don't know which existing login (username) it's for. Asks the user
  510.      * to select a username and confirm the password change.
  511.      *
  512.      * Note: The caller doesn't know the username for aNewLogin, so this
  513.      *       function fills in .username and .usernameField with the values
  514.      *       from the login selected by the user.
  515.      * 
  516.      * Note; XPCOM stupidity: |count| is just |logins.length|.
  517.      */
  518.     promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
  519.         const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  520.  
  521.         var usernames = logins.map(function (l) l.username);
  522.         var dialogText  = this._getLocalizedString("userSelectText");
  523.         var dialogTitle = this._getLocalizedString("passwordChangeTitle");
  524.         var selectedIndex = { value: null };
  525.  
  526.         // If user selects ok, outparam.value is set to the index
  527.         // of the selected username.
  528.         var ok = this._promptService.select(this._window,
  529.                                 dialogTitle, dialogText,
  530.                                 usernames.length, usernames,
  531.                                 selectedIndex);
  532.         if (ok) {
  533.             // Now that we know which login to change the password for,
  534.             // update the missing username info in the aNewLogin.
  535.  
  536.             var selectedLogin = logins[selectedIndex.value];
  537.  
  538.             this.log("Updating password for user " + selectedLogin.username);
  539.  
  540.             aNewLogin.username      = selectedLogin.username;
  541.             aNewLogin.usernameField = selectedLogin.usernameField;
  542.  
  543.             this._pwmgr.modifyLogin(selectedLogin, aNewLogin);
  544.         }
  545.     },
  546.  
  547.  
  548.  
  549.  
  550.     /* ---------- Internal Methods ---------- */
  551.  
  552.  
  553.  
  554.  
  555.     /*
  556.      * _getNotifyBox
  557.      *
  558.      * Returns the notification box to this prompter, or null if there isn't
  559.      * a notification box available.
  560.      */
  561.     _getNotifyBox : function () {
  562.         try {
  563.             // Get topmost window, in case we're in a frame.
  564.             var notifyWindow = this._window.top
  565.  
  566.             // Some sites pop up a temporary login window, when disappears
  567.             // upon submission of credentials. We want to put the notification
  568.             // bar in the opener window if this seems to be happening.
  569.             if (notifyWindow.opener) {
  570.                 var chromeWin = notifyWindow
  571.                                     .QueryInterface(Ci.nsIInterfaceRequestor)
  572.                                     .getInterface(Ci.nsIWebNavigation)
  573.                                     .QueryInterface(Ci.nsIDocShellTreeItem)
  574.                                     .rootTreeItem
  575.                                     .QueryInterface(Ci.nsIInterfaceRequestor)
  576.                                     .getInterface(Ci.nsIDOMWindow);
  577.                 var chromeDoc = chromeWin.document.documentElement;
  578.  
  579.                 // Check to see if the current window was opened with
  580.                 // chrome disabled, and if so use the opener window.
  581.                 if (chromeDoc.getAttribute("chromehidden")) {
  582.                     this.log("Using opener window for notification bar.");
  583.                     notifyWindow = notifyWindow.opener;
  584.                 }
  585.             }
  586.  
  587.  
  588.             // Find the <browser> which contains notifyWindow, by looking
  589.             // through all the open windows and all the <browsers> in each.
  590.             var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  591.                      getService(Ci.nsIWindowMediator);
  592.             var enumerator = wm.getEnumerator("navigator:browser");
  593.             var tabbrowser = null;
  594.             var foundBrowser = null;
  595.  
  596.             while (!foundBrowser && enumerator.hasMoreElements()) {
  597.                 var win = enumerator.getNext();
  598.                 tabbrowser = win.getBrowser(); 
  599.                 foundBrowser = tabbrowser.getBrowserForDocument(
  600.                                                   notifyWindow.document);
  601.             }
  602.  
  603.             // Return the notificationBox associated with the browser.
  604.             if (foundBrowser)
  605.                 return tabbrowser.getNotificationBox(foundBrowser)
  606.  
  607.         } catch (e) {
  608.             // If any errors happen, just assume no notification box.
  609.             this.log("No notification box available: " + e)
  610.         }
  611.  
  612.         return null;
  613.     },
  614.  
  615.  
  616.     /*
  617.      * _getLocalisedString
  618.      *
  619.      * Can be called as:
  620.      *   _getLocalisedString("key1");
  621.      *   _getLocalizedString("key2", ["arg1"]);
  622.      *   _getLocalizedString("key3", ["arg1", "arg2"]);
  623.      *   (etc)
  624.      *
  625.      * Returns the localized string for the specified key,
  626.      * formatted if required.
  627.      *
  628.      */ 
  629.     _getLocalizedString : function (key, formatArgs) {
  630.         if (formatArgs)
  631.             return this._strBundle.formatStringFromName(
  632.                                         key, formatArgs, formatArgs.length);
  633.         else
  634.             return this._strBundle.GetStringFromName(key);
  635.     },
  636.  
  637.  
  638.     // From /netwerk/base/public/nsNetUtil.h....
  639.     /**
  640.      * This function is a helper to get a protocol's default port if the
  641.      * URI does not specify a port explicitly. Returns -1 if protocol has no
  642.      * concept of ports or if there was an error getting the port.
  643.      */
  644.     _GetRealPort : function (aURI) {
  645.         var port = aURI.port;
  646.  
  647.         if (port != -1)
  648.             return port; // explicitly specified
  649.  
  650.         // Otherwise, we have to get the default port from the protocol handler
  651.         // Need the scheme first
  652.         var scheme = aURI.scheme;
  653.  
  654.         var ioService = Cc["@mozilla.org/network/io-service;1"].
  655.                         getService(Ci.nsIIOService);
  656.  
  657.         var handler = ioService.getProtocolHandler(scheme);
  658.         port = handler.defaultPort;
  659.  
  660.         return port;
  661.     },
  662.  
  663.  
  664.     // From: /embedding/components/windowwatcher/public/nsPromptUtils.h
  665.     // Args: nsIChannel, nsIAuthInformation, boolean, string, int
  666.     _GetAuthHostPort : function (aChannel, aAuthInfo) {
  667.  
  668.         // Have to distinguish proxy auth and host auth here...
  669.         var flags = aAuthInfo.flags;
  670.  
  671.         if (flags & (Ci.nsIAuthInformation.AUTH_PROXY)) {
  672.             // TODO: untested...
  673.             var proxied = aChannel.QueryInterface(Ci.nsIProxiedChannel);
  674.             if (!proxied)
  675.                 throw "proxy auth needs nsIProxiedChannel";
  676.  
  677.             var info = proxied.proxyInfo;
  678.             if (!info)
  679.                 throw "proxy auth needs nsIProxyInfo";
  680.  
  681.             var idnhost = info.host;
  682.             var port    = info.port;
  683.  
  684.             var idnService = Cc["@mozilla.org/network/idn-service;1"].
  685.                              getService(Ci.nsIIDNService);
  686.             host = idnService.convertUTF8toACE(idnhost);
  687.         } else {
  688.             var host = aChannel.URI.host;
  689.             var port = this._GetRealPort(aChannel.URI);
  690.         }
  691.  
  692.         return [host, port];
  693.     },
  694.  
  695.  
  696.     // From: /embedding/components/windowwatcher/public/nsPromptUtils.h
  697.     // Args: nsIChannel, nsIAuthInformation
  698.     _GetAuthKey : function (aChannel, aAuthInfo) {
  699.         var key = "";
  700.         // HTTP does this differently from other protocols
  701.         var http = aChannel.QueryInterface(Ci.nsIHttpChannel);
  702.         if (!http) {
  703.             key = aChannel.URI.prePath;
  704.             this.log("_GetAuthKey: got http channel, key is: " + key);
  705.             return key;
  706.         }
  707.  
  708.         var [host, port] = this._GetAuthHostPort(aChannel, aAuthInfo);
  709.  
  710.         var realm = aAuthInfo.realm;
  711.  
  712.         key += host;
  713.         key += ':';
  714.         key += port;
  715.  
  716.         this.log("_GetAuthKey got host: " + key + " and realm: " + realm);
  717.  
  718.         return [key, realm];
  719.     },
  720.  
  721.  
  722.     // From: /embedding/components/windowwatcher/public/nsPromptUtils.h
  723.     // Args: nsIAuthInformation, string, string
  724.     /**
  725.      * Given a username (possibly in DOMAIN\user form) and password, parses the
  726.      * domain out of the username if necessary and sets domain, username and
  727.      * password on the auth information object.
  728.      */
  729.     _SetAuthInfo : function (aAuthInfo, username, password) {
  730.         var flags = aAuthInfo.flags;
  731.         if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
  732.             // Domain is separated from username by a backslash
  733.             var idx = username.indexOf("\\");
  734.             if (idx == -1) {
  735.                 aAuthInfo.username = username;
  736.             } else {
  737.                 aAuthInfo.domain   =  username.substring(0, idx);
  738.                 aAuthInfo.username =  username.substring(idx+1);
  739.             }
  740.         } else {
  741.             aAuthInfo.username = username;
  742.         }
  743.         aAuthInfo.password = password;
  744.     }
  745.  
  746. }; // end of LoginManagerPrompter implementation
  747.  
  748. var component = [LoginManagerPromptFactory, LoginManagerPrompter];
  749. function NSGetModule(compMgr, fileSpec) {
  750.     return XPCOMUtils.generateModule(component);
  751. }
  752.  
  753.  
  754.